using System;
using System.Data;
using System.Web;
using System.Web.UI;
using System.Collections;
using System.IO;
using System.Drawing;
using System.Xml;
using OSGeo.MapServer;
using System.Data.OleDb;
using System.Runtime.Serialization;

namespace Gaiocorp.MapServer
{
  /// <summary>
  /// MapServer class. Contains all the map logic
  /// </summary>

	public class MapServer
	{
        private mapObj _aMapObj;
        private rectObj FullExtent;
        private rectObj PreviousExtent;
        private int IdentifyTolerance=0;
        private string _mapfile;
        private int _mapWidth = 0;
        private int _mapHeight = 0;
        private string _mapURL = string.Empty;
        private System.Drawing.Image _mapImg;

       

        #region Contructors
        public MapServer()
        {

        }

        /// <summary>
        ///  MapServer Constructor
        /// </summary>
        /// <param name="MapFile">MapFile name with full path</param>
        /// <param name="MapWidth">Width of map in pixels</param>
        /// <param name="MapHeight">Height of map in pixels</param>
		public MapServer(string MapFile, int MapWidth, int MapHeight)
		{
            try
            {
                _aMapObj = new mapObj(MapFile);
                _aMapObj.width = MapWidth;
                _aMapObj.height = MapHeight;
                //store initial extent
                try
                {
                    FullExtent = new rectObj(Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["InitXMin"].ToString()),
                                                Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["InitYMin"].ToString()),
                                                Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["InitXMax"].ToString()),
                                                Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["InitYMax"].ToString()),
                                                mapscript.MS_FALSE);
                }
                catch
                {
                    FullExtent = new rectObj(_aMapObj.extent.minx, _aMapObj.extent.miny, _aMapObj.extent.maxx, _aMapObj.extent.maxy, mapscript.MS_FALSE);
                }
                SetPreviousExtent();
            }
            catch (Exception ex)
            {
                WriteErrLog(ex);
                throw;
            }
            try
            {
                try
                {
                    //Check mapfile for tolerance value
                    IdentifyTolerance = Convert.ToInt32(_aMapObj.getMetaData("IDENTIFYTOLERANCE"));
                }
                catch (Exception)
                {
                    //Check web.config
                    IdentifyTolerance = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["IdentifyTolerance"].ToString());
                }
                
            }
            catch
            {
                // Default tolerance
                IdentifyTolerance = 10;
            }
        }
        #endregion

        #region Properties
        [DataMember] 
        public string MapFile
        {
            get { return _mapfile; }
            set { _mapfile = value; }
        }
       [DataMember] 
       public int MapWidth
        {
            get { return _mapWidth; }
            set { _mapWidth = value; }
        }
       [DataMember] 
        public int MapHeight
        {
            get { return _mapHeight; }
            set { _mapHeight = value; }
        }
       [DataMember] 
        public string MapURL
        {

            get { return _mapURL; }
            set { _mapURL = value; }
        }
       [DataMember] 
        public System.Drawing.Image MapImage
        {
            get { return _mapImg; }
            set { _mapImg = value; }
        }
        #endregion

        # region Zoom and Pan actions
           public void DoZoom(ZOOMMODE zoomMode)
            {
              SetPreviousExtent();
              //pointObj at center for zoomin and zoomout
              pointObj centerPoint = new pointObj(_aMapObj.width/2,_aMapObj.height/2,0,0);
              //Do Zoom In
              if(zoomMode==ZOOMMODE.ZoomIn)
              {
                _aMapObj.zoomPoint(2, centerPoint, _aMapObj.width, _aMapObj.height, _aMapObj.extent, null);
              }
              //Do Zoom Out
              if(zoomMode==ZOOMMODE.ZoomOut)
              {
                _aMapObj.zoomPoint(-2, centerPoint, _aMapObj.width, _aMapObj.height, _aMapObj.extent, null);
              }
            }
            public  void DoZoomBox(ZOOMMODE zoomMode, double xmin, double ymin, double xmax, double ymax)
            {
              SetPreviousExtent();
              if(zoomMode==ZOOMMODE.ZoomIn)
              {
                  string strFilename = "C:\\projects\\mapdata\\maplogs\\ms_log1.txt";
                  StreamWriter objStreamWriter;
                  objStreamWriter = File.AppendText(strFilename);

                  objStreamWriter.WriteLine("Calling GetZoomRectangle");
                 
                  rectObj rect = GetZoomRectangle(xmin, ymin, xmax, ymax);
                  objStreamWriter.WriteLine("Finished GetZoomRectangle");
                  objStreamWriter.WriteLine("Calling ZoomRectangle");
                  ZoomRectangle(rect);
                  objStreamWriter.WriteLine("Finished ZoomRectangle");
                  objStreamWriter.Close();
              }
              else
              {
                  rectObj rect = GetZoomRectangle(-xmin, -ymin, _aMapObj.height + xmax, _aMapObj.height + ymax);
                  ZoomRectangle(rect);
              }
            }
            public  void DoPan(Double xmin, Double ymin, Double xmax, Double ymax)
            {
              double deltaX = xmax - xmin;
              double deltaY = ymax - ymin;
              pointObj centerPoint = new pointObj(_aMapObj.width/2-deltaX,_aMapObj.height/2-deltaY,0,0);
              _aMapObj.zoomPoint(1, centerPoint, _aMapObj.width, _aMapObj.height, _aMapObj.extent, null);
              this.MapURL = GetURLMap();
            }
            public mapObj DoZoomFullExtent()
            {
              SetPreviousExtent();
              _aMapObj.extent=FullExtent;
              return _aMapObj;
            }
            public  void DoZoomPreviousExtent()
            { 
              rectObj actualExtent=new rectObj(_aMapObj.extent.minx,_aMapObj.extent.miny,_aMapObj.extent.maxx,_aMapObj.extent.maxy,mapscript.MS_FALSE);
              _aMapObj.extent=PreviousExtent;
              PreviousExtent=actualExtent;
            }

            /// <summary>
            /// Function for workaround at bug to be solved in next distributions of mapserver
            /// </summary>
            /// <param name="imgrect"></param>
           
            private void ZoomRectangle(rectObj imgrect)
            {
              //rectObj geoRect = pixel_rect2point_rect(new rectObj(imgrect.minx, imgrect.maxy, imgrect.maxx, imgrect.miny, mapscript.MS_FALSE));
              _aMapObj.zoomRectangle(imgrect, _aMapObj.width, _aMapObj.height, _aMapObj.extent, null);
            }

            private rectObj GetZoomRectangle(double xmin, double ymin, double xmax, double ymax)
            {
                double dAbsMaxY = ymax;
                double dAbsMinY = ymin;
                if (ymin < ymax)
                {
                    dAbsMaxY = ymin;
                    dAbsMinY = ymax;
                }
                
                try
                {
                    rectObj rect = new rectObj(xmin, dAbsMinY, xmax, dAbsMaxY, mapscript.MS_TRUE);
                    return rect;
                }
                catch (Exception ex)
                {

                    return null;
                }
                finally
                {
                }        
            }


            /// <summary>
            /// Set Previous Extent
            /// </summary>
            private void SetPreviousExtent()
            {
              PreviousExtent = new rectObj(_aMapObj.extent.minx,_aMapObj.extent.miny,_aMapObj.extent.maxx,_aMapObj.extent.maxy,mapscript.MS_FALSE);
            }

            public void DoZoomPoint(pointObj centerPoint)
            {
                _aMapObj.setExtent(centerPoint.x - 100, centerPoint.y - 50, centerPoint.x + 100, centerPoint.y + 50);
            }
           
            public void ZoomToScale(double scale, pointObj imgPoint)
            {
                _aMapObj.zoomScale(scale, imgPoint, _aMapObj.width, _aMapObj.height, FullExtent, FullExtent);
            }
            public void SetScale(double scale)
            {
                pointObj imgPoint = new pointObj(_aMapObj.width / 2, _aMapObj.height / 2, 0,0);
                _aMapObj.zoomScale(scale, imgPoint, _aMapObj.width, _aMapObj.height, FullExtent, FullExtent);
            }
        # endregion
        
        # region Draw Map images
            public  string GetURLMap()
            {
                //refresh map
                try
                {
                  errorObj errObj = new errorObj();
                  _aMapObj.freeQuery(-1);
                  for (int i = 0; i < _aMapObj.numlayers; i++)
                  {
                      layerObj lyr = _aMapObj.getLayer(i);
                  }
                  imageObj image = _aMapObj.draw();
                  _aMapObj.embedScalebar(image);
                  _aMapObj.drawLabelCache(image);
                  //be sure to have set Web parameters in mapfile (IMAGEPATH, IMAGEURL)
                  //explicitily set mappath
                  _aMapObj.mappath = image.imagepath;
                  
                    //return URLMap
                    string time = DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second.ToString() + DateTime.Now.Millisecond.ToString();
                    string ImageName = time + "." + _aMapObj.outputformat.extension;
                    image.save(ImageName, _aMapObj);
                    return image.imageurl + "/" + ImageName;
                }
                catch (Exception ex)
                {

                    WriteErrLog(ex);
                    throw;
                }

            }
            public static System.Drawing.Image ConvertByteArrayToImage(byte[] byteArray)
            {
                if (byteArray != null)
                {
                    MemoryStream ms = new MemoryStream();
                    ms.Write(byteArray, 0, byteArray.Length);
                    return System.Drawing.Image.FromStream(ms);
                }
                return null;
            }


            public String GetURLMapSelection(int active_layer_index)
            {
                try
                {
                    //Draw the map to get a handle on the imageObj
                    imageObj image=_aMapObj.draw();
                    layerObj active_layer = _aMapObj.getLayer(active_layer_index);
                    //Open the layer
                    active_layer.open();
                    //Get the selection
                    resultCacheObj results = active_layer.getResults();
                    //Loop through selection and draw each feature
                    for (int iResultsNo = 0; iResultsNo < results.numresults; iResultsNo++)
                    {
                        // Change for Mapserver 6.0 
                        //int shapeInd = results.getResult(iResultsNo).shapeindex;
                        //shapeObj shape = active_layer.getFeature(shapeInd, -1);
                        shapeObj shape = active_layer.getShape(results.getResult(iResultsNo));
                        if (shape.type == (int)MS_SHAPE_TYPE.MS_SHAPE_POINT)
                        { shape.draw(_aMapObj, _aMapObj.getLayerByName("selectedpoint"), image); }
                        else if (shape.type == (int)MS_SHAPE_TYPE.MS_SHAPE_LINE)
                        { shape.draw(_aMapObj, _aMapObj.getLayerByName("selectedline"), image); }
                        else if (shape.type == (int)MS_SHAPE_TYPE.MS_SHAPE_POLYGON)
                        { shape.draw(_aMapObj, _aMapObj.getLayerByName("selectedpoly"), image); }

                    }
                    //imageObj image=_aMapObj.drawQuery();
                    _aMapObj.embedScalebar(image);
                    _aMapObj.drawLabelCache(image);
                    _aMapObj.mappath = image.imagepath;
                    active_layer.close();
                    String time = DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second.ToString() + DateTime.Now.Millisecond.ToString();
                    String ImageName = time + "." + _aMapObj.outputformat.extension;
                    image.save(ImageName, _aMapObj);
                    return image.imageurl + "/" + ImageName;
                }
                catch (Exception ex)
                {
                    WriteErrLog(ex);
                    throw;
                }
            }
        #endregion

        #region Selections and Identify
            public XmlElement GetXMLSelection(layerObj aLayer, resultCacheObj results)
            {
                XmlDocument xmlLegendDoc = new XmlDocument();
                
                XmlElement elmResultSet = xmlLegendDoc.CreateElement("RESULTSET");
                string sLayerTitle = string.Empty;
                if (results != null)
                {
                    
                    if (results.numresults > 0)
                    {
                        try
                        {
                            sLayerTitle= aLayer.getMetaData("WMS_TITLE");
                            
                        }
                        catch
                        {
                            sLayerTitle = aLayer.name;
                        }
                        elmResultSet.SetAttribute("layertitle", sLayerTitle);
                        elmResultSet.SetAttribute("layername", aLayer.name);
                        elmResultSet.SetAttribute("count", results.numresults.ToString());
                        aLayer.open();
                        for (int iResultsNo = 0; iResultsNo < results.numresults; iResultsNo++)
                        {
                            XmlElement elmResult = xmlLegendDoc.CreateElement("RESULT");
                            int shapeInd = results.getResult(iResultsNo).shapeindex;
                            int tileInd = results.getResult(iResultsNo).tileindex;
                            try
                            {
                                string[] fa = aLayer.getMetaData("FIELDALIASES").Split(',');
                                for (int iFields = 0; iFields < aLayer.numitems; iFields++)
                                {
                                    string fname = aLayer.getItem(iFields);
                                    foreach (string item in fa)
                                    {
                                        if (item.Split(';')[0].ToString()==fname)
                                        {
                                            //Get field alias
                                            XmlElement elmField = xmlLegendDoc.CreateElement("SELECTEDFIELD");
                                            elmField.SetAttribute("name", item.Split(';')[1].ToString());
                                            shapeObj shape = aLayer.getShape(results.getResult(iResultsNo));
                                            //shapeObj shape = aLayer.getFeature(shapeInd, -1);
                                            string strValue = shape.getValue(iFields).ToString();
                                            elmField.SetAttribute("value", strValue);
                                            elmResult.AppendChild((XmlNode)elmField);
                                        }
                                    }
                                }

                            }
                            catch
                            {
                                for (int iFields = 0; iFields < aLayer.numitems; iFields++)
                                {
                                    XmlElement elmField = xmlLegendDoc.CreateElement("SELECTEDFIELD");
                                    elmField.SetAttribute("name", aLayer.getItem(iFields));
                                    shapeObj shape = aLayer.getShape(results.getResult(iResultsNo));
                                    //shapeObj shape = aLayer.getFeature(shapeInd, tileInd);
                                    //TODO: This returns null for postgis layers???
                                    string strValue = "<NULL>";
                                    if (shape.getValue(iFields) != null)
                                    {
                                        strValue = shape.getValue(iFields).ToString();
                                    }
                                    elmField.SetAttribute("value", strValue);
                                    elmResult.AppendChild((XmlNode)elmField);
                                }
                            }
                            //Now add the FID column
                            XmlElement elmFIDField = xmlLegendDoc.CreateElement("SELECTEDFIELD");
                            elmFIDField.SetAttribute("name", "FID");
                            elmFIDField.SetAttribute("value", shapeInd.ToString());
                            elmResult.AppendChild((XmlNode)elmFIDField);
                            elmResultSet.AppendChild((XmlNode)elmResult);
                        }
                        aLayer.close();
                    }
                }
                return elmResultSet;
            }
            /// <summary>
            /// Returns the results of a selection box as an xml element
            /// </summary>
            /// <param name="active_layer_index"></param>
            /// <param name="xmin"></param>
            /// <param name="ymin"></param>
            /// <param name="xmax"></param>
            /// <param name="ymax"></param>
            /// <returns>XMLElement</returns>
            public XmlElement DoSelectBox(int active_layer_index, Double xmin, Double ymin, Double xmax, Double ymax)
            {
                try
                {
                    layerObj active_layer = _aMapObj.getLayer(active_layer_index);
                    active_layer.template = "dummy"; //Need to set this in case the mapfile does not include a template definition
                    SetPreviousExtent();
                    rectObj rect = pixel_rect2point_rect(new rectObj(xmin, ymin, xmax, ymax, mapscript.MS_FALSE));
                    _aMapObj.freeQuery(-1);
                    //Query by rectangle
                    active_layer.queryByRect(_aMapObj, rect);
                    _aMapObj.querymap.status = mapscript.MS_ON;
                    resultCacheObj results = active_layer.getResults();
                    XmlElement xmlResults = GetXMLSelection(active_layer, results);
                    return xmlResults;
                }
                catch (Exception ex)
                {
                    WriteErrLog(ex);
                    throw;
                }
             }
            /// <summary>
            /// Returns the results of a point search as an XML Element
            /// </summary>
            /// <param name="active_layer_index"></param>
            /// <param name="x"></param>
            /// <param name="y"></param>
            /// <returns>XmlElement</returns>
            public  XmlElement DoIdentify(int active_layer_index, Double x, Double y)
            {
                try
                {
                    layerObj active_layer = _aMapObj.getLayer(active_layer_index);
                    active_layer.template = "dummy"; //Need to set this in case the mapfile does not include a template definition
                    SetPreviousExtent();
                    pointObj point = pixel2point(new pointObj(x, y, 0, 0));

                    //Query by point
                    active_layer.queryByPoint(_aMapObj, point, mapscript.MS_MULTIPLE, IdentifyTolerance);
                    _aMapObj.querymap.status = mapscript.MS_ON;

                    // Get the results
                    resultCacheObj results = active_layer.getResults();
                    XmlElement xmlResults = GetXMLSelection(active_layer, results);
                    return xmlResults;
                }
                catch (Exception ex)
                {
                    WriteErrLog(ex);
                    throw;
                    //return null;
                }
            }
            /// <summary>
            /// Returns true or false depending whether the layer is queryable or not
            /// </summary>
            /// <param name="layer">The layer object</param>
            /// <returns></returns>
            private static bool IsLayerQueryable(layerObj layer)
            {
                if (layer.type == MS_LAYER_TYPE.MS_LAYER_TILEINDEX)
                    return false;

                if (layer.connectiontype != MS_CONNECTION_TYPE.MS_OGR && layer.connectiontype != MS_CONNECTION_TYPE.MS_ORACLESPATIAL && layer.connectiontype != MS_CONNECTION_TYPE.MS_POSTGIS && layer.connectiontype != MS_CONNECTION_TYPE.MS_SDE && layer.connectiontype != MS_CONNECTION_TYPE.MS_SHAPEFILE)
                {
                    return false;
                }
                if (layer.template != null && layer.template.Length > 0) return true;

                for (int i = 0; i < layer.numclasses; i++)
                {
                    if (layer.getClass(i).template != null && layer.getClass(i).template.Length > 0)
                        return true;
                }
                return false;
            }

            public resultCacheObj DoQueryByAttributes(string layer_name, string query_string)
            {
                resultCacheObj results = new resultCacheObj();
                layerObj layer = _aMapObj.getLayerByName(layer_name);
                layer.template = "dummy";
                layerObj clonedlayer = layer.clone();
                _aMapObj.insertLayer(clonedlayer, 0);
                clonedlayer.template = "dummy";
                //Always zoom to selected features
                bool zoomToResults = true;
                if (layer != null)
                {
                    try
                    {
                        if (IsLayerQueryable(layer))
                        {
                            //Console.WriteLine("Layer [" + i + "] name: " + layer.name);
                            string[] retval = BuildQuery(layer, query_string);
                            _aMapObj.freeQuery(-1);
                            // Do the Query by attributes first on the cloned
                            // layer in case there are errors
                            clonedlayer.queryByAttributes(_aMapObj, retval[0], retval[1], mapscript.MS_MULTIPLE);
                            
                            //If we didnt get an error do the query on the actual layer
                            layer.queryByAttributes(_aMapObj, retval[0], retval[1], mapscript.MS_MULTIPLE);
                            //string s = layer.generateSLD();
                            _aMapObj.querymap.status = mapscript.MS_ON;
                            // zoom to the query results
                            results = layer.getResults();
                            if (zoomToResults == true)
                                ZoomToSelected(results);
                            return results;
                        }
                        else return null;

                    }
                    catch (Exception ex)
                    {
                        WriteErrLog(ex);
                        throw;
                        //return null;
                    }
                    finally
                    {
                        _aMapObj.removeLayer(0);
                        clonedlayer.Dispose();
                        _aMapObj.querymap.status = mapscript.MS_OFF;
                    }
                }
                else return null;
            }

            private static string[] BuildQuery(layerObj layer, string qstring)
            {
                string[] retval = { string.Empty, string.Empty };
                if (layer != null && layer.map != null)
                {
                    string qs = qstring;
                    string att = null;

                    if (layer.connectiontype == MS_CONNECTION_TYPE.MS_SHAPEFILE)
                    {
                        //If its a shapefile we need to setup split the query string
                        // into the query column (att variable) and the value (qs variable)
                        char[] sOps = new char[4];
                        sOps[0] = '=';
                        sOps[1] = '>';
                        sOps[2] = '<';
                        if (qs != string.Empty)
                        {
                            if (qs.IndexOfAny(sOps) != -1)
                            {
                                att = qs.Substring(0, qs.IndexOfAny(sOps)).Trim();
                                int sI = qs.IndexOfAny(sOps);
                                string qs1 = qs.Substring(sI, qs.Length - sI);
                                qs = qs1.TrimStart(sOps).Trim();
                            }
                        }
                    }
                    if (layer.connectiontype == MS_CONNECTION_TYPE.MS_ORACLESPATIAL)
                    {
                        qs = qs.Replace(@"""", @"'");
                    }
                    //Console.WriteLine("Query string: " + qs);
                    retval[0] = att;
                    retval[1] = qs;
                    

                }
               return retval;
            }

            private void ZoomToSelected(resultCacheObj results)
            {
                rectObj query_bounds = null;
                    
                if (results != null && results.numresults > 0)
                {
                    // calculating the extent of the results
                    if (query_bounds == null)
                        query_bounds = new rectObj(results.bounds.minx - 100, results.bounds.miny - 50,
                            results.bounds.maxx + 100, results.bounds.maxy + 50, 0);
                   else
                    {
                        if (results.bounds.minx < query_bounds.minx) query_bounds.minx = results.bounds.minx;
                        if (results.bounds.miny < query_bounds.miny) query_bounds.miny = results.bounds.miny;
                        if (results.bounds.maxx > query_bounds.maxx) query_bounds.maxx = results.bounds.maxx;
                        if (results.bounds.maxy > query_bounds.maxy) query_bounds.maxy = results.bounds.maxy;
                    }
                }
                // setting the map extent to the result bounds
                if (query_bounds != null)
                {
                    _aMapObj.setExtent(query_bounds.minx, query_bounds.miny, query_bounds.maxx, query_bounds.maxy);
                }
                
            }
            public void DoSelectByIndex(string active_layer_name, int iShapeIndex)
            {
                layerObj active_layer = _aMapObj.getLayerByName(active_layer_name);
                active_layer.template = "dummy"; //Need to set this in case the mapfile does not include a template definition
                SetPreviousExtent();
                active_layer.queryByIndex(_aMapObj, -1, iShapeIndex, mapscript.MS_FALSE);
                _aMapObj.querymap.status = mapscript.MS_ON;
                
                // Get the results
                resultCacheObj results = active_layer.getResults();
                ZoomToSelected(results);
               
            }

            public XmlElement GetAttributeTableAsXML(string sLayerName)
            {
                layerObj aLayer = _aMapObj.getLayerByName(sLayerName);
                aLayer.queryByRect(_aMapObj, FullExtent);
                // Get the results
                resultCacheObj results = aLayer.getResults();
                
                XmlElement elmResultSet = GetXMLSelection(aLayer, results);
                return elmResultSet;
            }
        #endregion

        # region Utilities
            /// <summary>
            /// Convert pixel rectangle coordinates to map rectangle coordinates
            /// </summary>
            /// <param name="pixelRect">pixel rectangle (from map Image)</param>
            /// <returns></returns>
            private rectObj pixel_rect2point_rect(rectObj pixelRect)
            {
                rectObj extent = _aMapObj.extent;
                double mapWidth = extent.maxx - extent.minx;
                double mapHeight = extent.maxy - extent.miny;

                double x_min_perc;
                double x_max_perc;
                double y_min_perc;
                double y_max_perc;
                x_min_perc = pixelRect.minx / _aMapObj.width;
                x_max_perc = pixelRect.maxx / _aMapObj.width;
                y_min_perc = (_aMapObj.height - pixelRect.maxy) / _aMapObj.height;
                y_max_perc = (_aMapObj.height - pixelRect.miny) / _aMapObj.height;

                double x_min = extent.minx + x_min_perc * mapWidth;
                double x_max = extent.minx + x_max_perc * mapWidth;
                double y_min = extent.miny + y_min_perc * mapHeight;
                double y_max = extent.miny + y_max_perc * mapHeight;

                rectObj mapRect = new rectObj(x_min, y_min, x_max, y_max, mapscript.MS_FALSE);
                return mapRect;
            }

            public double GetScale()
            {
                return _aMapObj.scaledenom;
            }

            public void SetMapSize(int intWidth, int intHeight)
            {
                _aMapObj.width = intWidth;
                _aMapObj.height = intHeight;
            }

            // Returns Real Extent Coordinates from Pixel Coordnates
            public int[] GetXYCoordinates(int x, int y)
            {
                int[] result = new int[2];

                //convert the image point in map point
                pointObj point = pixel2point(new pointObj(x, y, 0, 0));

                result[0] = (int)point.x;
                result[1] = (int)point.y;
                AddPoint(new pointObj(point.x, point.y, 0, 0), string.Empty);
                return result;
            }

            public void SetLayerStatus(string strLayerName, int intStatus)
            {
                layerObj lyr = _aMapObj.getLayerByName(strLayerName);
                lyr.status = intStatus;

            }

            /// <summary>
            /// Conver pixel point coordinates to map point coordinates
            /// </summary>
            /// <param name="pointPixel">pixel point (from map Image)</param>
            /// <returns></returns>
            private pointObj pixel2point(pointObj pointPixel)
            {
                rectObj extent = _aMapObj.extent;
                double mapWidth = extent.maxx - extent.minx;
                double mapHeight = extent.maxy - extent.miny;
                double xperc;
                double yperc;
                xperc = pointPixel.x / _aMapObj.width;
                yperc = (_aMapObj.height - pointPixel.y) / _aMapObj.height;
                double x = extent.minx + xperc * mapWidth;
                double y = extent.miny + yperc * mapHeight;
                pointObj pointMap = new pointObj(x, y, 0, 0);
                return pointMap;
            }
            private void WriteErrLog(Exception x)
            {
                string strFilename = System.Configuration.ConfigurationManager.AppSettings["ErrorLogFile"].ToString();
                //Get a StreamReader class that can be used to read the file
                StreamWriter objStreamWriter;
                objStreamWriter = File.AppendText(strFilename);
                //Append the the current date and time at the end of the string followed by the exception"
                objStreamWriter.WriteLine(DateTime.Now.ToString() + " MAP SERVER ERROR: " + x.Message);
                objStreamWriter.WriteLine("Stack Trace:");
                objStreamWriter.WriteLine("\n" + x.StackTrace);
                //Close the stream
                objStreamWriter.Close();
            }

            public string[] GetMapSize()
            {
                string[] mapsize = { _aMapObj.width.ToString(), _aMapObj.height.ToString() };
                return mapsize;
            }

            public string[] GetCurrentMapExtent()
            {
                string[] mapsize = { _aMapObj.extent.minx.ToString(), _aMapObj.extent.miny.ToString(),_aMapObj.extent.maxx.ToString(),_aMapObj.extent.maxy.ToString() };
                return mapsize;
            }

            public void CreateLayer()
            {
                layerObj lyr = new layerObj(_aMapObj);
                lyr.connectiontype = MS_CONNECTION_TYPE.MS_INLINE;
                lyr.name = "pin";
                lyr.type = MS_LAYER_TYPE.MS_LAYER_POINT;
                lyr.status = mapscript.MS_DEFAULT;
                classObj pinClass = new classObj(lyr);
                styleObj pinStyle=new styleObj(pinClass);
                pinStyle.setSymbolByName(_aMapObj, "pin");
            }

        # endregion

        # region Add/Remove Points

            public  void AddPoint(pointObj targetPoint, string strPointLabel)
            {
                shapeObj shp = targetPoint.toShape();
                shp.text = strPointLabel;
                layerObj lyrPushPin = _aMapObj.getLayerByName("pin");
                if (lyrPushPin == null)
                {
                    CreateLayer();
                    lyrPushPin = _aMapObj.getLayerByName("pin");
                }
               int s = lyrPushPin.addFeature(shp);
            }

            public  mapObj RemovePoint( string mapfile)
            {
            rectObj savedExtent = _aMapObj.extent;
            int lyrOldPushPinIdx=-1;
            layerObj lyrNewPushPin = new layerObj(_aMapObj);
            // Remove any layers w/out a name
            for (int i = 0; i < _aMapObj.numlayers; i++)
            {
                layerObj lyr = _aMapObj.getLayer(i);
                if (lyr.name == null || lyr.name==string.Empty)
                {
                    _aMapObj.removeLayer(lyr.index);

                }
               
               // System.Diagnostics.Debug.WriteLine(lyr.name);
            }
            for (int i = 0; i < _aMapObj.numlayers; i++)
            {
                layerObj lyr = _aMapObj.getLayer(i);
                if (lyr.name == "pin")
                {
                    lyrOldPushPinIdx = i;
                }
            }
            _aMapObj.removeLayer(lyrOldPushPinIdx);
            
            //Recreate the map object which should now have the pin layer empty
            CreateLayer();
            _aMapObj.extent = savedExtent; 
            return _aMapObj;
            }
       # endregion
     
        #region Legend Specific
            public XmlElement GetLegend(int activeIdx, bool created)
            {
                MapUtilities m = new MapUtilities();
                XmlDocument xmlLegendDoc = new XmlDocument();
                XmlElement elmLegend = xmlLegendDoc.CreateElement("LEGEND");
                
                for (int i = 0; i < _aMapObj.numlayers; i++)
                {
                    layerObj lyr = _aMapObj.getLayer(i);

                    if (lyr.name != "pin" && lyr.name != "selectedline" && lyr.name != "selectedpoly" && lyr.name != "selectedpoint" && lyr.name != "__embed__scalebar" && m.CanDisplayLayer(lyr.name))
                    {
                        
                        lyr.open();
                        XmlElement elmLayer = xmlLegendDoc.CreateElement("LAYER");
                        try
                        {
                            elmLayer.SetAttribute("title", lyr.getMetaData("WMS_TITLE"));
                        }
                        catch
                        {
                            elmLayer.SetAttribute("title", lyr.name);
                        }
                        try
                        {
                            //To be used for the tooltip when moving the mouse around the map?
                            elmLayer.SetAttribute("idfield", lyr.getMetaData("IDENTIFYFIELD"));
                            elmLayer.SetAttribute("idname", lyr.getMetaData("IDENTIFYNAME"));
                        }
                        catch
                        {
                            elmLayer.SetAttribute("idfield", "NONE");
                            elmLayer.SetAttribute("idname", "NONE");
                        }
                        try
                        {
                            elmLayer.SetAttribute("xlinkfield", lyr.getMetaData("XLINKFIELD"));
                        }
                        catch
                        {
                            elmLayer.SetAttribute("xlinkfield", "NONE");
                        }
                        if (lyr.type == MS_LAYER_TYPE.MS_LAYER_ANNOTATION)
                        {
                            elmLayer.SetAttribute("annotation", "1");
                        }
                        else
                        {
                            elmLayer.SetAttribute("annotation", "0");
                        }
                        if (lyr.type == MS_LAYER_TYPE.MS_LAYER_TILEINDEX || lyr.type == MS_LAYER_TYPE.MS_LAYER_RASTER || lyr.type == MS_LAYER_TYPE.MS_LAYER_QUERY || lyr.type == MS_LAYER_TYPE.MS_LAYER_CIRCLE || lyr.type == MS_LAYER_TYPE.MS_LAYER_CHART || lyr.type == MS_LAYER_TYPE.MS_LAYER_ANNOTATION)
                        {
                            elmLayer.SetAttribute("canbeactive", "0");
                        }
                        else
                        {
                            elmLayer.SetAttribute("canbeactive", "1");
                        }

                        try
                        {
                            elmLayer.SetAttribute("hlink", lyr.getMetaData("HYPERLINKITEM"));
                        }
                        catch
                        {
                            elmLayer.SetAttribute("hlink", "NONE");
                        }

                        try
                        {
                            elmLayer.SetAttribute("hparams", lyr.getMetaData("HYPERLINKPARAMS"));
                        }
                        catch
                        {
                            elmLayer.SetAttribute("hparams", "NONE");
                        }

                        elmLayer.SetAttribute("name", lyr.name);
                        elmLayer.SetAttribute("group", lyr.group);
                        elmLayer.SetAttribute("isvisible", lyr.status.ToString());
                        elmLayer.SetAttribute("index", i.ToString());
                        if (i == activeIdx)
                            elmLayer.SetAttribute("isactive", "1");
                        else
                            elmLayer.SetAttribute("isactive", "0");
                        elmLegend.AppendChild((XmlNode)elmLayer);
                        XmlElement elmFields = xmlLegendDoc.CreateElement("FIELDS");
                       
                        // Get field names for this layer
                        for (int iFields = 0; iFields < lyr.numitems; iFields++)
                        {
                            XmlElement elmField = xmlLegendDoc.CreateElement("FIELD");
                            elmField.SetAttribute("name", lyr.getItem(iFields));
                            elmFields.AppendChild(elmField);
                        }
                       
                        elmLayer.AppendChild(elmFields);
                        // Get classes (symbols) for this layer
                        XmlElement elmClasses = xmlLegendDoc.CreateElement("CLASSES");
                        for (int j = 0; j < lyr.numclasses; j++)
                        {
                            XmlElement elmClass = xmlLegendDoc.CreateElement("CLASS");
                            classObj clsLayerClass = lyr.getClass(j);
                            elmClass.SetAttribute("name", clsLayerClass.name);
                            if (created == false)
                            {
                                string symURL = GetURLSymbol(lyr, j);
                                elmClass.SetAttribute("symbolURL", symURL);
                                elmClasses.AppendChild(elmClass);
                            }
                        }
                        elmLayer.AppendChild(elmClasses);
                        if (lyr.connectiontype == MS_CONNECTION_TYPE.MS_WFS || lyr.connectiontype == MS_CONNECTION_TYPE.MS_WMS)
                        {
                            //Its a WxS layer type. Create sublayer elements
                            try
                            {
                                string[] subLayers = lyr.getMetaData("wms_name").Split(',');
                                XmlElement elmSubLayers = xmlLegendDoc.CreateElement("SUBLAYERS");
                                foreach (string subLayer in subLayers)
                                {
                                    XmlElement elmSubLayer = xmlLegendDoc.CreateElement("SUBLAYER");
                                    elmSubLayer.SetAttribute("name", subLayer);
                                    elmSubLayers.AppendChild(elmSubLayer);
                                }
                                elmLayer.AppendChild(elmSubLayers);
                            }
                            catch {}
                        }

                    }
                    else
                    {
                        if (lyr.name != "pin" && lyr.name != "selectedline" && lyr.name != "selectedpoint" && lyr.name != "selectedpoly")
                        {
                            lyr.status = mapscript.MS_OFF;
                        }
                    }
                    lyr.close();
                }
                return elmLegend;
            }
           
            public String GetURLSymbol(layerObj lyr, int classIdx)
            {
                int legWidth =-1;
                int legHeight =-1;
                try
                {
                    legWidth = _aMapObj.legend.width;
                    legHeight = _aMapObj.legend.height;
                    if (legWidth == 0 || legHeight == 0)
                    {
                        legWidth = 16;
                        legHeight = 16;
                    
                    }

                }
                catch 
                {
                    // Default values in case there is no legend
                    // setup in the mapfile
                    legWidth = 16;
                    legHeight = 16;
                }
                try
                {
                    classObj clsLayerClass = lyr.getClass(classIdx);
                    imageObj imgLayerLegend = clsLayerClass.createLegendIcon(_aMapObj, lyr, legWidth, legHeight);
                    //clsLayerClass.drawLegendIcon(_aMapObj, lyr, legWidth, legHeight, imgLayerLegend, 0, 0);
                    System.Web.UI.WebControls.Image img = new System.Web.UI.WebControls.Image();
                    
                    String time = DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second.ToString() + DateTime.Now.Millisecond.ToString();
                    imgLayerLegend.save(imgLayerLegend.imagepath + "\\" + time + "_leg" + lyr.index.ToString() + classIdx.ToString() + ".png", _aMapObj);
                    img.ImageUrl = imgLayerLegend.imageurl + "/" + time + "_leg" + lyr.index.ToString() + classIdx.ToString() + ".png";

                    return img.ImageUrl;
                }
                catch (Exception ex)
                {

                    WriteErrLog(ex);
                    //throw;
                    return null;
                }

            }
        #endregion

        # region HypeLink
            public string getURL(double x, double y, string sLyrName, string sLinkField, string sLinkParams)
            {
                string sUrl = string.Empty;
                try
                {
                    layerObj active_layer = _aMapObj.getLayerByName(sLyrName);
                    active_layer.template = "dummy"; //Need to set this in case the mapfile does not include a template definition
                    SetPreviousExtent();
                    pointObj point = pixel2point(new pointObj(x, y, 0, 0));

                    //Query by point
                    active_layer.queryByPoint(_aMapObj, point, mapscript.MS_MULTIPLE, IdentifyTolerance);
                    _aMapObj.querymap.status = mapscript.MS_ON;

                    // Get the results
                    resultCacheObj results = active_layer.getResults();
                    if (results == null)
                    {
                        return string.Empty;
                    }
                    else
                    {
                        if (results.numresults > 0)
                        {
                            // Open layer
                            active_layer.open();
                            int shapeInd = results.getResult(0).shapeindex;
                            //shapeObj shape = active_layer.getFeature(shapeInd, -1);
                            shapeObj shape = active_layer.getShape(results.getResult(0));
                            try
                            {
                                // 1st iteration to find the URL field- Loop through fields and get field names and values
                                for (int iFields = 0; iFields < active_layer.numitems; iFields++)
                                {
                                    //Get field name
                                    string strFieldName = active_layer.getItem(iFields);
                                    if (strFieldName == sLinkField)
                                    {
                                        //Get value
                                        string strValue = shape.getValue(iFields).ToString();
                                        sUrl = strValue;
                                        //If we have params add the ? in the url string
                                        if (sLinkParams != "NONE")
                                        {
                                            sUrl = sUrl + "?";
                                        }
                                    }
                                }
                                if (sLinkParams != "NONE")
                                {
                                    // Get parameter/field combinations (in case there are any)
                                    //string strURLParamFields = string.Empty;
                                    string[] strArrParamFields = { };
                                    ArrayList arrURLParamFields = new ArrayList();
                                    ArrayList arrURLParams = new ArrayList();
                                    ArrayList arrURLFields = new ArrayList();
                                    strArrParamFields = sLinkParams.Split(';');
                                    for (int i = 0; i < strArrParamFields.Length; i++)
                                    {
                                        arrURLParamFields.Add(strArrParamFields[i]);
                                    }
                                    for (int i = 0; i < arrURLParamFields.Count; i++)
                                    {
                                        if (i % 2 == 0) // Even number- Populate the Params array
                                        {
                                            arrURLParams.Add(arrURLParamFields[i]);
                                        }
                                        else //Populate the fields array
                                        {
                                            arrURLFields.Add(arrURLParamFields[i]);
                                        }
                                    }
                                    // 2nd iteration for parameters- Loop through fields and get field names and values
                                    for (int iFields = 0; iFields < active_layer.numitems; iFields++)
                                    {
                                        //Get field name
                                        string strFieldName = active_layer.getItem(iFields);
                                        if (arrURLFields.IndexOf(strFieldName) != -1)
                                        {
                                            //Get value
                                            string strValue = shape.getValue(iFields).ToString();
                                            sUrl = sUrl + arrURLParams[arrURLFields.IndexOf(strFieldName)] + "=" + strValue.Trim() + "&";
                                        }
                                    }
                                    sUrl = sUrl.Substring(0, sUrl.Length - 1);
                                }
                            }
                            catch (Exception ex)
                            {
                                WriteErrLog(ex);
                                return null;
                            }
                            active_layer.close();
                        }
                        else return string.Empty;
                    }
                    return sUrl;
                }
                catch (Exception ex)
                {
                    WriteErrLog(ex);
                    return string.Empty;
                }
            }
        #endregion

        #region Print Map

            public string[] PrintMap(string PrintScale, string sPaperSize)
            {
                mapObj printMap = _aMapObj.clone();
                switch (sPaperSize)
                {
                    case "A4P":
                        printMap.width = 470;
                        printMap.height = 650;
                        break;
                    case "A4L":
                        printMap.width = 700;
                        printMap.height = 500;
                        break;
                    case "A3P":
                        printMap.width = 760;
                        printMap.height = 1100;
                        break;
                    case "A3L":
                        printMap.width = 1000;
                        printMap.height = 700;
                        break;
                }
                if (PrintScale != string.Empty)
                {
                    pointObj pntCenter = new pointObj(printMap.width / 2, printMap.height / 2, 0, 0);
                    printMap.zoomScale(Convert.ToInt32(PrintScale), pntCenter, printMap.width, printMap.height, printMap.extent, printMap.extent);
                }
                else
                    PrintScale = String.Format("{0:0.00}",_aMapObj.scaledenom);
                imageObj image = printMap.draw();
                printMap.embedScalebar(image);
                printMap.drawLabelCache(image);
                printMap.mappath = image.imagepath;
                //Create Print Map
                String mName = "PMAP_" + DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second.ToString() + DateTime.Now.Millisecond.ToString();
                String ImageName = mName + "." + printMap.outputformat.extension;
                //image.save(image.imagepath + "\\" + ImageName, map);
                image.save(ImageName, printMap);
                //Create Print Legend
                legendObj leg = printMap.legend;
                leg.imagecolor = new colorObj(255, 255, 255, 1);
                imageObj legimg = printMap.drawLegend();
                String lName = "PMAP_leg_" + DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second.ToString() + DateTime.Now.Millisecond.ToString();
                String lImageName = lName + "." + printMap.outputformat.extension;
                legimg.save(lImageName, printMap);
                string[] returnString = { printMap.web.imageurl + "/" + ImageName, printMap.web.imageurl + "/" + lImageName, PrintScale };
                return returnString;
            }
        # endregion

        #region Edit shapefiles
            
            /// <summary>
            /// NOT USED (yet)
            /// </summary>
            /// <param name="pntX"></param>
            /// <param name="pntY"></param>
            /// <param name="active_layer_index"></param>
            public void AddPointToLayer(double pntX, double pntY, int active_layer_index)
            {
                layerObj aLayer = _aMapObj.getLayer(active_layer_index);
                pointObj aPnt = new pointObj(pntX, pntX, 0, 0);
                pointObj aGeoPnt = pixel2point(aPnt);
                shapeObj shp = aPnt.toShape();
                if (aLayer.connectiontype == MS_CONNECTION_TYPE.MS_SHAPEFILE && aLayer.type== MS_LAYER_TYPE.MS_LAYER_POINT)
                {
                    string sShpFile = aLayer.data;
                    mapObj aMap = aLayer.map;
                    string sShpPath = aMap.shapepath;
                    shapefileObj ms_newShapefileObj = new shapefileObj(sShpPath+"\\"+sShpFile, -2);
                    ms_newShapefileObj.addPoint(aGeoPnt);
                    //generate the sql INSERT statment
                    //get field list and value list to use in the query on dbf
                    //string fieldList = "";
                    //string valueList = "";
                    //for (int i = 0; i < (fieldValues.Length / 2); i++)
                    //{
                    //    fieldList = fieldList + fieldValues[i, 0];
                    //    valueList = valueList + "'" + fieldValues[i, 1] + "'";
                    //    if (i < ((fieldValues.Length / 2) - 1))
                    //    {
                    //        fieldList = fieldList + ", ";
                    //        valueList = valueList + ", ";
                    //    }
                    //}
                    ////add record for dbf table
                    //String sqlInsert = "INSERT INTO " + sShpFile + " (" + fieldList + ") VALUES(" + valueList + ")";
                    //OleDbConnection cn = new OleDbConnection(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + sShpPath + "\\" + sShpFile + ";Extended Properties=dBASE IV;User ID=Admin;Password=");
                    //cn.Open();
                    //OleDbCommand com = cn.CreateCommand();
                    //com.CommandText = sqlInsert;
                    //com.CommandType = CommandType.Text;
                    //com.ExecuteNonQuery();
                    //cn.Close();
                    ms_newShapefileObj.Dispose();

                }
                
            }
            /// <summary>
            /// NOT USED (yet)
            /// </summary>
            /// <param name="active_layer_index"></param>
            /// <param name="pntX"></param>
            /// <param name="pntY"></param>
            /// <returns></returns>
            public XmlElement NewRecAsXML(int active_layer_index, int pntX, int pntY)
            {
                layerObj aLayer = _aMapObj.getLayer(active_layer_index);
                aLayer.open();
                XmlDocument xmlLegendDoc = new XmlDocument();

                XmlElement elmTable = xmlLegendDoc.CreateElement("TABLE");
                for (int iFields = 0; iFields < aLayer.numitems; iFields++)
                {
                    XmlElement elmField = xmlLegendDoc.CreateElement("TABFIELD");
                    elmField.SetAttribute("name", aLayer.getItem(iFields));
                    string strValue = string.Empty;
                    elmField.SetAttribute("value", strValue);
                    elmTable.AppendChild((XmlNode)elmField);
                }
                aLayer.close();
                return elmTable;
            }
        #endregion
    }
}
